package com.example.concurrentprogramming8016.ReentrantReadWriteLock;

import lombok.extern.slf4j.Slf4j;

import java.util.TreeMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author liyanhao
 * @Date 2023-01-07 16:20
 * ReentrantReadWriteLock 更新缓存并且进行锁降级的实例
 */
@Slf4j
public class CachedData {
    Object data;
    volatile boolean cachaeValid;
    final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

    void processCachedData() throws InterruptedException {
        TimeUnit.SECONDS.sleep(1);
        rwl.readLock().lock();
        TimeUnit.SECONDS.sleep(1);
        System.out.println("第一次添加读锁-共享锁");
        if (!cachaeValid) {
            //在获取写锁之前 必须释放读锁
            TimeUnit.SECONDS.sleep(1);
            System.out.println("检查数据后释放读锁");
            rwl.readLock().unlock();
            TimeUnit.SECONDS.sleep(1);

            System.out.println("添加写锁进行更新操作");
            rwl.writeLock().lock();
            try {
                //重新检查状态，因为另一个线程可能已经获取了写锁定并在我们之前更改了状态。
                TimeUnit.SECONDS.sleep(1);

                System.out.println("更新之前检查数据是否有变化");
                if (!cachaeValid) {
                    TimeUnit.SECONDS.sleep(1);

                    System.out.println("修改数据 并修改数据检查状态为true");
                    data = "123";
                    cachaeValid = true;
                }
                //通过在释放写锁定之前获取读锁定来降级
                TimeUnit.SECONDS.sleep(1);
                System.out.println("在对写锁进行降级之前先获取读锁");
                rwl.readLock().lock();
            } finally {
                System.out.println("释放写锁");
                TimeUnit.SECONDS.sleep(1);
                rwl.writeLock().unlock();//释放写锁
            }
        }
        //使用读锁进行读取使用数据操作
        try {
            System.out.println("在读锁中进行数据操作");
            TimeUnit.SECONDS.sleep(2);
            use(data);
        } finally {
            //释放读锁
            System.out.println("释放读锁完成数据的使用");
            rwl.readLock().unlock();
        }
    }

    public void getData() {
        rwl.readLock().lock();
        try {
            System.out.println("---------读锁使用数据中:" + data);
        } finally {
            rwl.readLock().unlock();
        }
    }

    public void setData() {
        rwl.writeLock().lock();
        try {
            System.out.println("----------写锁修改数据中:" + data);
            data = "setData";
        } finally {
            rwl.writeLock().unlock();
        }
    }

    private void use(Object data) {
        System.out.println("使用" + data + "吃屎");
    }

    public static void main(String[] args) throws InterruptedException {
        CachedData cachedData = new CachedData();
        new Thread(() -> {
            try {
                cachedData.processCachedData();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }).start();
        for (; ; ) {
            TimeUnit.MILLISECONDS.sleep(500);
            cachedData.getData();
            System.out.println("每0.5秒进行一下读取操作");
        }
    }


}

